iT邦幫忙

2022 iThome 鐵人賽

DAY 23
2

情境

在網頁內容很豐富的情境中,頁面上放置導覽列是很重要的功能,如下圖:

假設這是一個新聞或論壇網站,左邊 aside 部分就是我們的導覽列,上面有網站的 Brand logo,然後就是各主題的分類,包含 Home, News, Sport, Reel, Travel, Future 等各種主題。

那右邊 main 區塊,就是內容的主要部分,從圖中我們可以看到,因為內容很長,所以會需要有一個滾動條讓我們往下拉,才能夠看到下面的內容:

那當我們將頁面往下拉之後,會發現,左邊的 aside 導覽列其實就會看不到了。

假設一個情境,如果我們使用者在瀏覽網頁的時候,意猶未盡,很想要多看到更多不同類別的內容,但每次都需要把滾動條滑回最上面才能夠看到導覽列,這樣的操作會造成使用者在瀏覽的過程當中相當不便。特別是在使用者需要很頻繁的換頁找資料或瀏覽,並且每一頁的主要內容都很長,每次好不容易滑到最底部的時候,要換頁卻要再花好一番功夫才能滑到最上面看到導覽列。

為了解決這個困擾,我們就會想要讓左邊導覽列能夠「釘在畫面上」固定的位置,這樣不管滾動條滑到哪裡,導覽列也會跟著走,非常的方便。

你能看見多遠的未來呢?

我們來看看這樣的應用該怎麼實作。

首先來看一下主要的 html 結構:

<div class="wrapper">
  <aside>...</aside>
  <main>...</main>
</div>

那為了將導覽列 aside 跟主要區塊 main 做左右並排的排列,或許我們會考慮方便做網格佈局的 CSS Grid 來幫我們做到這件事,這麼想也是很合理的,以下是我們寫出來的 CSS:

.wrapper {
  display: grid;
  grid-template-columns: 250px minmax(10px, 1fr);
  grid-gap: 20px;
}

從這個 CSS 可以看到,我們將外容器 wrapper 宣告為一個 grid 容器,然後對內元件的排列作規範,左邊的 aside 寬度為 250px,右邊主要內容區塊則設置他的最小最大寬度 minmax(10px, 1fr),這樣能夠符合響應式的設計,最後給他一個 20px 的間隔。

接下來,我們要來實作釘選導覽列,我們可以利用 CSS 的 position 屬性。position 支援很多的功能,其中 sticky 可以幫助我們讓元件釘在最近的 scrolling ancestor 上,也就是最接近的可滾動元件。在 MDN 上的說明如下:

Sticky positioning is a hybrid of relative and fixed positioning. The element is treated as relative positioned until it crosses a specified threshold, at which point it is treated as fixed positioned.

既然如此,我們就可以把這屬性寫在導覽列的元件上:

aside {
  position: sticky;
  top: 0;
}

但是,神奇的事情發生了,我們此時會發現,導覽列並沒有我們預期的能夠釘在畫面上!!!

你一開始想必會懷疑是不是自己 CSS 抄錯了所以不會動,或是 html 結構不對。但是最後,如果順利的話,你會發現下面這個狀況,為了方便觀察,我們把導覽列 aside 也加上背景顏色:

我們發現,導覽列的高度居然跟主要內容區塊的高度是一樣的!這個應該就是造成他無法釘住的主要原因。

因為當我們對一個外容器宣告為 display: grid; 時,外容器當中的 align-items 屬性,預設值為 stretch,意思是 將內容元素撐開至 grid 容器大小

我們換個情境來看,假設今天我們用有一組網格排列的卡片,你就不會覺得那麼奇怪了:

<div class="cards">
  <div class="card">...</div>
  <div class="card">...</div>
  <div class="card">...</div>
</div>
.cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  grid-gap: 20px;
}

一樣使用的是 CSS Grid,一樣外容器中 align-items 的預設值是 stretch,但是在卡片的情境當中,我們就會覺得非常的整齊美觀。

既然如此,我們就來調整這個預設值就可以了。

調整的方法有兩種,一個是透過外容器來解決,我們將外容器的 align-items 設為 start

.wrapper {
  display: grid;
  grid-template-columns: 250px minmax(10px, 1fr);
  grid-gap: 20px;
  align-items: start; /* 覆蓋掉原本的預設值 stretch */
}

另一個方式,我們也可以針對內元件單獨做設置:

aside {
  position: sticky;
  top: 0;
  align-self: start; /* 針對內元件單獨個別做設置 */
}

我們就能夠達到我們預期的「釘選導覽列」效果了:

小結

這裡我們介紹了 CSS Grid 外容器當中,因為忽略預設值 align-items: stretch; 所造成的釘選導覽列失效問題。

這個預設值的效果,在卡片情境當中,我們從來不會覺得他奇怪,但是當他被放在釘選導覽列情境的時候,可能我們就會想不通為什麼會有這個奇怪的預設值,而容易忽略這個預設值而造成問題。

所以,適時的記憶一些容易被忽略的預設值,有助於幫助我們避免這些問題。


上一篇
【Day22】Grid - 自動填滿效果
下一篇
【Day24】Flexbox - 使用 justify-content: space-between 佈局
系列文
防禦性 CSS - 建立「防患未然」的匠人心態30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言